package com.example.demo.datasource.multi.config;


import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Configuration
public class MultiDataSourceConfig {


    //(1) 配置数据源
        /*
        @Bean : 将其表示为spring的bean,让spring进行管理
        @Primary : 指定同一个接口有多个实现类可以注入的时候 , 默认注入哪一个
        @ConfigurationProperties : 绑定配置文件中的属性到bean中
        */

    @Bean
    @ConfigurationProperties(prefix = "jdbc.test1")
    public DataSource test1DataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "jdbc.test2")
    public DataSource test2DataSource(){
        return DruidDataSourceBuilder.create().build();
    }


 /* 实现WEB监控的配置处理
  http://127.0.0.1:xxxx/druid/index.html
*/

    @Bean
    public ServletRegistrationBean druidServlet() {
        // 现在要进行druid监控的配置处理操作
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
                new StatViewServlet(), "/druid/*");
        // 白名单,多个用逗号分割， 如果allow没有配置或者为空，则允许所有访问
        servletRegistrationBean.addInitParameter("allow", "");
        // 黑名单,多个用逗号分割 (共同存在时，deny优先于allow)
        servletRegistrationBean.addInitParameter("deny", "");
        // 控制台管理用户名
        servletRegistrationBean.addInitParameter("loginUsername", "root");
        // 控制台管理密码
        servletRegistrationBean.addInitParameter("loginPassword", "root");
        // 是否可以重置数据源，禁用HTML页面上的“Reset All”功能
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean ;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;
        filterRegistrationBean.setFilter(new WebStatFilter());
        //所有请求进行监控处理
        filterRegistrationBean.addUrlPatterns("/*");
        //添加不需要忽略的格式信息
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*");
        return filterRegistrationBean ;
    }



    //(2) 配置SqlSessionFactory
        /*
        每一个SqlSessionFactory都设置了一个DataSource属性 , 所以他们产生的SqlSession就是不同数据库的SqlSession ;
        另外TypeAliasesPackage属性表示扫描该属性下的全部java文件作为别名 , 即mapper.xml文件中 select 标签 resultType属性的值 ,
        如果不设置则要输入对应实体的全限定名称 , 设置之后只需输入对应实体的类名即可;
        MapperLocations 属性用于扫描指定文件下的哪些文件作为MyBatis的xml映射文件;
        */
    @Bean
    @Primary
    public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource")DataSource test1DataSource) throws Exception{
        SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
        fb.setDataSource(test1DataSource);
        fb.setTypeAliasesPackage("com.example.demo.datasource");
        fb.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/test1/**/*.xml")  //sql映射文件位置
        );
        fb.setConfigLocation(
                new PathMatchingResourcePatternResolver().getResource("classpath:/mapper/mybatis-config.xml")   //这里面主要配置驼峰命名规则，不想开启则可以把这一项去掉
        );
        return fb.getObject();
    }

    @Bean
    public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource")DataSource test2DataSource) throws Exception{
        SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
        fb.setDataSource(test2DataSource);
        fb.setTypeAliasesPackage("com.example.demo.datasource");
        fb.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/test2/**/*.xml")
        );
        return fb.getObject();
    }

    //(3)配置MapperScannerConfigurer
        /*
        MapperScannerConfigurer用于配置MyBatis Mapper接口的扫描 ;
        该对象可以设置BasePackage 和 AnnotationClass属性,分别表示 扫描哪个包下的文件且标识了指定注解的接口 作为MyBatis 的 Mapper ,
        同时还需设置一个SqlSessionFactory;
        */
    @Bean(name="test1MapperScannerConfigurer")
    public MapperScannerConfigurer test1MapperScannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setSqlSessionFactoryBeanName("test1SqlSessionFactory");
        configurer.setBasePackage("com.example.demo.datasource");
        configurer.setAnnotationClass(test1Dao.class);
        return configurer;
    }

    @Bean(name="test2MapperScannerConfigurer")
    public MapperScannerConfigurer test2MapperScannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setSqlSessionFactoryBeanName("test2SqlSessionFactory");
        configurer.setBasePackage("com.example.demo.datasource");
        configurer.setAnnotationClass(test2Dao.class);
        return configurer;
    }

       //自定义的运行器且只能注解在类上的注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Component
    public @interface test1Dao {
        String value() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Component
    public @interface test2Dao {
        String value() default "";
    }

    //(4)配置 事务管理器   事务管理器只需要指定对应的DataSource即可
    @Bean(name="test1TransactionManager")
    @Primary
    public DataSourceTransactionManager test1TransactionManager(@Qualifier("test1DataSource")DataSource test1DataSource) throws Exception{
        return new DataSourceTransactionManager(test1DataSource);
    }

    @Bean(name="test2TransactionManager")
    @Primary
    public DataSourceTransactionManager test2TransactionManager(@Qualifier("test2DataSource")DataSource test2DataSource) throws Exception{
        return new DataSourceTransactionManager(test2DataSource);
    }







}
